package sfeir.gwt.ergosoom.server.service.rest;

import java.io.Serializable;
import java.util.Properties;
import java.util.Random;
import java.util.logging.Logger;
import java.util.regex.Pattern;

import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;

import org.restlet.data.Form;
import org.restlet.data.MediaType;
import org.restlet.data.Status;
//import org.restlet.engine.util.DigestUtils;
import org.restlet.representation.Representation;
import org.restlet.representation.StringRepresentation;
import org.restlet.resource.Get;
import org.restlet.resource.Post;
import org.restlet.resource.ServerResource;

import sfeir.gwt.ergosoom.client.model.Email;
import sfeir.gwt.ergosoom.client.model.Profile;
import sfeir.gwt.ergosoom.server.service.ProfileAccess;

import com.google.appengine.api.datastore.DatastoreService;
import com.google.appengine.api.datastore.DatastoreServiceFactory;
import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.Query;
import com.google.appengine.api.datastore.Query.FilterOperator;
import com.google.appengine.api.memcache.Expiration;
import com.google.appengine.api.memcache.MemcacheService;
import com.google.appengine.api.memcache.MemcacheServiceFactory;

public class RegisterService extends ServerResource {

    private static final String REGISTRATION_EMAIL_FROM = "registration@ergosoom.com";
    private static final int DURATION_DAY = 24 * 3600;
    private static final Logger logger = Logger.getLogger(RegisterService.class.getName());

    @Get
    public Representation confirm() {
        MemcacheService cache = MemcacheServiceFactory.getMemcacheService();
//        DatastoreService ds = DatastoreServiceFactory.getDatastoreService();
        String sCode = (String) getRequest().getAttributes().get("code");
        if (null == sCode) {
            setStatus(Status.CLIENT_ERROR_BAD_REQUEST, "Invalid request");
            return new StringRepresentation("Invalid request");
        }
        Long code = Long.valueOf(sCode);

//        Entity entity = ds.prepare(new Query("TempPerson").addFilter("code", FilterOperator.EQUAL, code)).asSingleEntity();
        TempPerson tempPerson = (TempPerson) cache.get(code);
//        TempPerson tempPerson = new TempPerson(entity);
        cache.delete(code);
//        ds.delete(entity.getKey());
        cache.delete(tempPerson.email);
        if (null == tempPerson) {
            setStatus(Status.CLIENT_ERROR_BAD_REQUEST, "Confirmation code expired");
            return new StringRepresentation("Confirmation code expired");
        }

        Profile p = tempPerson.toProfile();
        new ProfileAccess().save(p);

//        return new ObjectRepresentation<Profile>(p);
        return new StringRepresentation("Subcription to Ergosoom complete. You may now setup your profile with Hive Got Contacts : <a href=\"hivereg://ergosoom/"+p.getEmail()+"/"+p.getPassword()+"\">Configure Hive Got Contacts</a>", MediaType.TEXT_HTML);
    }

    @Post
    public String signup(Representation entity) {
        Form form = new Form(entity);
        for (String s : new String[] { "email", "pass"}) {
            String value = form.getFirstValue(s);
            if (null == value || 0 == value.trim().length()) {
                setStatus(Status.CLIENT_ERROR_BAD_REQUEST, s + " is required");
                return "Invalid submission. " + s + " is required";
            }
        }
        String email = form.getFirstValue("email");
        String pass = form.getFirstValue("pass");
        String firstname = form.getFirstValue("firstname");
        String lastname = form.getFirstValue("lastname");
        String emailRegEx = "^([0-9a-zA-Z]([-.\\w]*[0-9a-zA-Z])*@([0-9a-zA-Z][-\\w]*[0-9a-zA-Z]\\.)+[a-zA-Z]{2,9})$";
        if (!Pattern.matches(emailRegEx, email)) {
            setStatus(new Status(Status.CLIENT_ERROR_BAD_REQUEST, "Invalid e-mail"), "Invalid e-mail");
            return "Invalid e-mail";
        }

        DatastoreService ds = DatastoreServiceFactory.getDatastoreService();
        int matching = ds.prepare(new Query("Profile").addFilter("email", FilterOperator.EQUAL, email)).countEntities();
        if (0 < matching) {
            setStatus(Status.CLIENT_ERROR_UNAUTHORIZED, "Account already registered !");
            return "Already registered !";
        }
        Long code = Math.abs(new Random(System.currentTimeMillis() - email.hashCode()).nextLong());
        MemcacheService cache = MemcacheServiceFactory.getMemcacheService();
        cache.put(code, new TempPerson(email, pass, firstname, lastname), Expiration.byDeltaSeconds(DURATION_DAY));
        cache.put(email, code, Expiration.byDeltaSeconds(DURATION_DAY));
//        ds.put(new TempPerson(email, pass, firstname, lastname).buildEntity());
        logger.warning(email + " --> " + code);
        // TODO body template (Velocity ?)
        String body = "Click here to continue registration : <a href=\"http://www.ergosoom.com/rest/confirm/" + code
                + "\">http://www.ergosoom.com/rest/confirm/" + code + "</a>";
        // TODO : admin email in properties
        try {
            Properties props = new Properties();
            Session session = Session.getDefaultInstance(props, null);
            MimeMessage msg = new MimeMessage(session);
            // Message msg = new Message();
            msg.setFrom(new InternetAddress(REGISTRATION_EMAIL_FROM));
            msg.addRecipient(Message.RecipientType.TO, new InternetAddress(email));
            msg.setSubject("Ergosoom subscription");
            msg.setContent(body, "text/html");
            Transport.send(msg);
            // ms.send(msg);
        } catch (AddressException e) {
            e.printStackTrace();
            return "Invalid e-mail";
        } catch (MessagingException e) {
            logger.severe(e.toString());
        }
        return "An confirmation e-mail has been send.";
    }

    @SuppressWarnings( { "serial", "unused" })
    private static class TempPerson implements Serializable {
        private String email;
        private String firstname;
        private String lastname;
        private String password;

        public TempPerson() {
        }

        public Entity buildEntity() {
            return null;
        }

        public TempPerson(String email, String password, String firstname, String lastname) {
            this.password = password;
            this.email = email;
            this.firstname = firstname;
            this.lastname = lastname;
        }

        public TempPerson(Entity entity) {
            if (!"TempPerson".equals(entity.getKind()))
                throw new IllegalArgumentException("must be a TempPerson entity");
            this.password = (String) entity.getProperty("password");
            this.email = (String) entity.getProperty("email");
            this.firstname = (String) entity.getProperty("firstname");
            this.lastname = (String) entity.getProperty("lastname");
        }

        public String getEmail() {
            return email;
        }

        public void setEmail(String email) {
            this.email = email;
        }

        public String getFirstname() {
            return firstname;
        }

        public void setFirstname(String firstname) {
            this.firstname = firstname;
        }

        public String getLastname() {
            return lastname;
        }

        public void setLastname(String lastname) {
            this.lastname = lastname;
        }

        public String getPassword() {
            return password;
        }

        public void setPassword(String password) {
            this.password = password;
        }

        public Profile toProfile() {
            Profile result = new Profile(this.firstname, this.lastname, email);
            result.getPerson().getEmails().add(new Email(this.email));
            result.setPassword(password);
            return result;
        }
    }
}
